# -*- coding: utf-8 -*-

import sip
sip.setapi('QString', 2)
sip.setapi('QStringList', 2)
sip.setapi('QVariant', 2)

from PyQt4 import QtCore

#_______________________________________________________________________________
class PlaylistTreeItem(object):
    def __init__(self, data=None, parent=None):
        self.parent_item = parent
        self.child_items = []
        self.item_data = {"state": "stop", "artist": "", "album": "", "albumartist": "", 
                          "title": "", "track": "", "name": "", "genre": "", 
                          "date": "", "composer": "", "performer": "", 
                          "comment": "", "disc": "", "file": "", "id": "",
                          "pos": "", "time": ""}
        if data is not None:
            self.item_data.update(data)
    
    def child(self, row):
        return self.child_items[row]
    
    def children(self):
        return self.child_items
    
    def childCount(self):
        return len(self.child_items)
    
    def insertChild(self, row, item):
        self.child_items.insert(row, item)
    
    def appendChild(self, item):
        self.child_items.append(item)
    
    def popChild(self, row):
        return self.child_items.pop(row)
    
    def removeChildren(self, position, count):
        if position < 0 or position + count > len(self.child_items):
            return False
        for row in xrange(count):
            self.child_items.pop(position)
        return True
    
    def insertChildren(self, position, count):
        if position < 0 or position > len(self.child_items):
            return False
        for row in xrange(count):
            item = PlaylistTreeItem(parent = self)
            self.child_items.insert(position, item)
        return True
    
    def columnCount(self):
        return 1
    
    def data(self, label=None):
        if label is None:
            return self.item_data
        if label in self.item_data:
            return self.item_data[label]
        else:
            return False
    
    def setData(self, label, value):
        if label in self.item_data:
            self.item_data[label] = value
            return True
        else:
            return False
    
    def parent(self):
        return self.parent_item
    
    def row(self):
        if self.parent_item:
            return self.parent_item.child_items.index(self)
        return 0
    
    def children_iterator(self):
        return iter(self.child_items)
#_______________________________________________________________________________
class PlaylistTreeModel(QtCore.QAbstractItemModel):
    moved_songs = QtCore.pyqtSignal((list))
    added_songs = QtCore.pyqtSignal((list, int))
    
    def __init__(self, parent=None):
        QtCore.QAbstractItemModel.__init__(self, parent)
        self.labels = ['state', 'id', 'track', 'title', 'artist', 'album', 'date']
        self.root_item = PlaylistTreeItem()
        self.idrow_map = {}
    
    def update_idrow_map(self):
        self.idrow_map = {}
        for item in self.root_item.children_iterator():
            self.idrow_map[item.data()["id"]] = item.row()
        
#    def reload_data(self):
#        count = self.root_item.childCount()
#        parent = QtCore.QModelIndex()
#        self.beginRemoveRows(parent, 0, count)
#        self.root_item.removeChildren(0, count)
#        self.endRemoveRows()
#        self.setup_data(self.root_item)
#        self.update_item_state()      
   
    def add_songs(self, songs):
        if len(songs) == 0:
            return False
        begin_row = int(songs[0]["pos"])
        count = len(songs)
        parent = QtCore.QModelIndex()
        self.beginInsertRows(parent, begin_row, begin_row + count -1)
        for song in songs:
            row_in = int(song["pos"])
            item_in = PlaylistTreeItem(song, self.root_item)
            self.root_item.insertChild(row_in, item_in)
        self.endInsertRows()
        self.update_idrow_map()
        self.pl_length = self.rowCount()
        return True

    def remove_songs(self, songs, diff):
        #FIXME: migliorare, ottimizzare
        if songs:
            if not self.move_songs(songs):
                return False
        begin_row = self.rowCount() - diff
        count = diff
        if not self.removeRows(begin_row, count):
            return False
        self.pl_length = self.rowCount()
        return True

    def move_songs(self, songs):
        parent = QtCore.QModelIndex()
        citems = [(self.root_item.child(self.idrow_map[song["id"]]), int(song["cpos"])) 
                   for song in songs]
        for citem, row_in in citems:
            row_out = citem.row()
            if row_in != row_out:
                self.beginMoveRows(parent, row_out, row_out, parent, row_in)
                self.root_item.popChild(row_out)
                self.root_item.insertChild(row_in, citem)
                self.endMoveRows()
        self.update_idrow_map()
        return True            
 
    def update_item_state(self, id, state):
        self.layoutAboutToBeChanged.emit()
        for item in self.root_item.children_iterator():
            if item.data("id") != id:
                item.setData(label="state", value="stop")
            else:
                item.setData(label="state", value=state)
        self.layoutChanged.emit()
       
    def rowCount(self, parent=None):
        return self.root_item.childCount()
    
    def columnCount(self, parent):
        return len(self.labels)
    
    def data(self, index, role):
        if not index.isValid(): 
            return None
        if role != QtCore.Qt.DisplayRole: 
            return None
        item = index.internalPointer()
        data = item.data(self.labels[index.column()])
        if self.labels[index.column()] == "state":
            if data == "stop":
                return unicode("")
            elif data == "play":
                return unicode(">")
            else:
                return unicode("||")
        return data.decode("utf-8")

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if role != QtCore.Qt.EditRole:
            return False
        if not index.isValid():
            return False
        item = self.root_item.child(index.row())
        label = self.labels[index.column()]
        result = item.setData(label, value)
        if result:
            self.dataChanged.emit(index, index)
        return result    

    def supportedDropActions(self):
        return QtCore.Qt.MoveAction | QtCore.Qt.CopyAction

    def insertRows(self, row, count, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            parent_item = self.root_item
        else:
            parent_item = parent.internalPointer()
        self.beginInsertRows(parent, row, row + count - 1)
        parent_item.insertChildren(row, count)
        self.endInsertRows()
        return True
    
    def insertRow(self, row, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            parent_item = self.root_item
        else:
            parent_item = parent.internalPointer()
        self.beginInsertRows(parent, row, row)
        parent_item.insertChildren(row, 1)
        self.endInsertRows()
        return True
    
    def removeRows(self, row, count, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            parent_item = self.root_item
        else:
            parent_item = parent.internalPointer()
        self.beginRemoveRows(parent, row, row + count - 1)
        parent_item.removeChildren(row, count)
        self.endRemoveRows()
        return True
    
    def flags(self, index):
        default_flags = QtCore.QAbstractItemModel.flags(self, index)
        if not index.isValid():
            return QtCore.Qt.ItemIsDropEnabled | default_flags
        return QtCore.Qt.ItemIsDragEnabled| QtCore.Qt.ItemIsEnabled | \
                default_flags
    
    def mimeTypes(self):
        types = ["application/x-novocaine-move-songs", 
                 "application/x-novocaine-add-songs"]
        return types
        
    def mimeData(self, indexes):
        mime_data = QtCore.QMimeData()
        item_data = QtCore.QByteArray()
        stream_data = QtCore.QDataStream(item_data, QtCore.QIODevice.WriteOnly)
        ids = []
        for index in indexes:
            if index.isValid():
                item = self.root_item.child(index.row())
                id = item.data("id")
                if id not in ids:
                    ids.append(id)
                    stream_data.writeString(id)
        mime_data.setData("application/x-novocaine-move-songs", item_data)
        return mime_data
    
    def dropMimeData(self, data, action, row, column, parent):
        if action == QtCore.Qt.IgnoreAction:
            return True
        if not data.hasFormat("application/x-novocaine-move-songs") and \
            not data.hasFormat("application/x-novocaine-add-songs"):
            return False
        if row != -1:
            begin_row = row
        else:
            begin_row = self.rowCount(QtCore.QModelIndex())
        if data.hasFormat("application/x-novocaine-move-songs"):
            mime_data = data.data("application/x-novocaine-move-songs")
            stream_data = QtCore.QDataStream(mime_data, QtCore.QIODevice.ReadOnly)
            ids = []
            while not stream_data.atEnd():
                ids.append(stream_data.readString())
            # ordino gli ids in base alle righe che occupano
            ids.sort(key=lambda x: self.idrow_map[x])
            #`devo applicare lo strateggemma seguente perché qt usa il numero di  riga
            # per determinare la posizione in cui spostare le righe, mentre mpd la posizione
            # nella playlist
            movements = []
            pos = begin_row - 1
            for id in ids:
                row_current = self.idrow_map[id]
                if row_current > pos:
                    pos += 1
                movements.append((id, pos))
            self.moved_songs.emit(movements)
        else:
            mime_data = data.data("application/x-novocaine-add-songs")
            stream_data = QtCore.QDataStream(mime_data, QtCore.QIODevice.ReadOnly)
            songs = []
            while not stream_data.atEnd():
                songs.append(stream_data.readString().encode("utf8"))
            self.added_songs.emit(songs, begin_row)
        return False 
       
    def index(self, row, column, parent=QtCore.QModelIndex()):
        if not self.hasIndex(row, column, parent):
            return QtCore.QModelIndex()
        if not parent.isValid():
            parent_item = self.root_item
        else:
            parent_item = parent.internalPointer()
        child_item = parent_item.child(row)
        if child_item:
            return self.createIndex(row, column, child_item)
        else:
            return QtCore.QModelIndex()

    def parent(self, child):
        if not child.isValid():
            return QtCore.QModelIndex()
        return QtCore.QModelIndex()

    def headerData(self, section, orientation, role):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return self.labels[section]
        else:
            return None
    
    def get_selected_songs(self, indexes):
        songs = [index.data() for index in indexes if self.labels[index.column()] == "id"]
        return songs
    
    def clear(self):
        self.beginResetModel()
        self.root_item.child_items = []
        self.endResetModel()
        self.pl_length = 0
        
    def setup_data(self, songs):
        #ordiniamo secondo la posizione della canzone nella playlist
        songs.sort(key=lambda x: int(x.get("pos")))
        parent = QtCore.QModelIndex()
        self.beginInsertRows(parent, 0, len(songs) - 1)              
        for song in songs:
            item = PlaylistTreeItem(song, self.root_item)
            self.root_item.appendChild(item)
        self.endInsertRows()
        self.update_idrow_map()
        self.pl_length = self.rowCount()
